# -*- coding: utf-8 -*-
import re
# douyin.py 顶部，加在最前面
import os, sys
# from datetime import time
import time
# from .browser import douyin_browser
import requests
from playwright.sync_api import sync_playwright
from typing import Dict, List, Tuple, Optional
from playwright.sync_api import sync_playwright, Route, Request
from playwright.sync_api import Page, Response
from playwright.sync_api import sync_playwright

# from playwright_stealth import stealth_sync
DETAIL_URL = "https://www.douyin.com/aweme/v1/web/aweme/detail/"

proxies = {"http": None, "https": None}


# 造参数
def build_params(aweme_id: str) -> dict:
    # 只给“相对固定”的参数，a_bogus / msToken 由浏览器环境自动补齐
    return {
        "device_platform": "webapp",
        "aid": "6383",
        "channel": "channel_pc_web",
        "aweme_id": aweme_id,
        "version_code": "190500",
        "browser_name": "Edge",
        "os_name": "Windows",
    }


# 主动发fetch请求
# def fetch_detail_in_page(page, aweme_id: str) -> dict:
#     js = """
#     async (input) => {
#       const qs = new URLSearchParams(input.params).toString();
#       const r = await fetch(input.url + '?' + qs, {
#         credentials: 'include',
#         headers: { 'Accept': 'application/json, text/plain, */*', 'Referer': 'https://www.douyin.com/' }
#       });
#       const t = await r.text();
#       try { return { ok: r.ok, status: r.status, url: r.url, data: JSON.parse(t) }; }
#       catch(e){ return { ok: r.ok, status: r.status, url: r.url, raw: t.slice(0, 800) }; }
#     }
#     """
#     res = page.evaluate(js, {"url": DETAIL_URL, "params": build_params(aweme_id)})
#     if not res.get("ok") or "data" not in res:
#         raise RuntimeError(f"detail 失败 status={res.get('status')}, 片段={res.get('raw', '')[:200]}")
#     return res  # 含 {ok,status,url,data}


# 拉字典
def fetch_detail_via_context(context, aweme_id: str) -> dict:
    """用 Playwright 的 request API 拉取 detail，继承浏览器的代理/UA/Cookie。"""
    params = build_params(aweme_id)
    headers = {
        "Referer": "https://www.douyin.com/",
        "Accept": "application/json, text/plain, */*",
    }

    last_err = None
    for attempt in range(1, 4):
        try:
            resp = context.request.get(
                DETAIL_URL,
                params=params,
                headers=headers,
                timeout=15000,
            )
            status = resp.status
            if not resp.ok:  # 注意：属性，不是方法
                raise RuntimeError(f"detail失败 status={status}, text={resp.text()[:200]}")

            txt = resp.text()
            if not txt:
                raise RuntimeError("detail返回空 body")

            try:
                data = resp.json()
                return {"ok": True, "status": status, "data": data}
            except Exception:
                return {"ok": True, "status": status, "raw": txt[:800]}
        except Exception as e:
            last_err = e
            time.sleep(0.3)  # 用 sleep 替代 context.wait_for_timeout

    raise RuntimeError(f"detail获取连续失败: {last_err}")




# 拦截参数
SAFE_BLOCK_TYPES = {"image", "media", "font", "stylesheet", "other"}  # 渲染大头，直接拦
BLOCK_HOSTS = {
    "mon.zijieapi.com",  # 监控/埋点 batch
    "mcs.zijieapi.com",  # webid/list 反复上报
    "privacy.zijieapi.com",  # CMP/隐私同意
    "lf-security.bytegoofy.com",  # 安全/指纹 SDK
    "lf-rc1.yhgfb-cn-static.com"  # 验证码中心（如未触发尽量不拉）
}
ALLOW_HOST_SUFFIXES = (
    "douyin.com",  # 主站：文档 + detail XHR
    "douyinstatic.com",  # 前端框架/播放器分片
    "bytednsdoc.com",  # 有时静态资源会走这里
    "bytescm.com", "bytetos.com"  # 字节系静态 CDN（框架/播放器）
)


# 拦截函数

def route_handler(route: Route, request: Request):
    """
    路由拦截规则：
    - 必须允许页面的 HTML 与关键 JS/XHR 执行（否则无法生成 a_bogus）；
    - 尽可能中止图片/媒体/字体/样式等重资源；
    - 仅放行目标主域，第三方域名直接拦截，减少体量与指纹噪音。
    """
    url = request.url
    host = (request.url.split("/")[2]).split(":")[0] if "://" in url else ""
    rtype = request.resource_type

    # 1) detail 必须放行（最关键）
    if "/aweme/v1/web/aweme/detail" in url:
        return route.continue_()

    # 2) 按类型拦大头
    if rtype in SAFE_BLOCK_TYPES:
        return route.abort()

    # 3) 样式表可“空响应”加速 onload（避免长等）
    if rtype == "stylesheet":
        return route.fulfill(status=200, body="/* noop */", content_type="text/css")

    # 4) 按域名拦监控/隐私/指纹等（不影响拿 detail）
    if host in BLOCK_HOSTS:
        return route.abort()

    # 5) 白名单域名正常放行（主站 + 核心静态）
    if host.endswith(ALLOW_HOST_SUFFIXES):
        return route.continue_()

    # if '.js' in url:
    #     return route.abort()

    # 6) 其他第三方：保守放行一次；若想更狠可改成 abort()
    return route.continue_()


# 开浏览器拿字典


def open_page(aweme_id, browser, context):
    page = context.new_page()

    # 使用 stealth，修复 webdriver / canvas / audio 等指纹
    # stealth_sync(page)

    # 再补一遍 webdriver 标志去除（双保险）
    page.add_init_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")

    # 伪装 UA-CH 等请求头（按 Edge 140 伪装）
    context.set_extra_http_headers({
        "Accept-Language": "zh-CN,zh;q=0.9",
        "sec-ch-ua": '"Chromium";v="140", "Microsoft Edge";v="140", "Not_A Brand";v="24"',
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": '"Windows"',
        "sec-ch-ua-full-version-list": '"Chromium";v="140.0.0.0", "Microsoft Edge";v="140.0.0.0", "Not_A Brand";v="24.0.0.0"',
    })

    # 只进主页“点火”，拿 Cookie / msToken / 安全 SDK
    page.goto("https://www.douyin.com/", wait_until="domcontentloaded")

    # 更稳：不走页内 JS，直接用 context.request
    res = fetch_detail_via_context(context,aweme_id)
    data = res["data"]

    page.close()
    return data


def _is_h265(br_item: Dict) -> bool:
    # 抖音里 is_h265==1 基本就是 H.265/HEVC；is_bytevc1==1 是 ByteVC1（抖音自家编解码）
    return bool(br_item.get("is_h265"))


def _codec_name(br_item: Dict) -> str:
    if br_item.get("is_h265"):
        return "H.265/HEVC"
    if br_item.get("is_bytevc1"):
        return "ByteVC1"
    return "H.264/AVC"


def list_candidates(aweme_detail: Dict) -> List[Dict]:
    """从 aweme_detail 中取出所有清晰度候选项，补充常用字段以便排序与展示"""
    video = aweme_detail.get("aweme_detail", aweme_detail).get("video", {})
    br_list = video.get("bit_rate") or []
    out = []
    for br in br_list:
        pa = br.get("play_addr", {})
        urls = pa.get("url_list") or []
        if not urls:
            continue
        out.append({
            "height": int(pa.get("height") or 0),
            "width": int(pa.get("width") or 0),
            "fps": int(br.get("FPS") or 0),
            "bitrate_kbps": int(br.get("bit_rate") or 0) // 1000 if br.get("bit_rate") else 0,
            "gear_name": br.get("gear_name") or "",
            "quality_type": br.get("quality_type"),
            "hdr": bool(br.get("HDR_bit")),
            "codec": _codec_name(br),
            "is_h265": _is_h265(br),
            "url_list": urls,
            "url": urls[0],  # 默认给第一个
        })
    # 补充：有些视频只给了 play_addr_h264 或 play_addr_265，可作为“备胎”
    for key, codec_label in [("play_addr_h264", "H.264/AVC"), ("play_addr_265", "H.265/HEVC")]:
        pa = video.get(key) or {}
        urls = pa.get("url_list") or []
        if urls:
            out.append({
                "height": int(pa.get("height") or 0),
                "width": int(pa.get("width") or 0),
                "fps": 30,  # 常见默认
                "bitrate_kbps": int((video.get("meta") or "").isdigit() and video["meta"]) or 0,
                "gear_name": key,
                "quality_type": None,
                "hdr": False,
                "codec": codec_label,
                "is_h265": (key == "play_addr_265"),
                "url_list": urls,
                "url": urls[0],
            })
    return out


# 挑选最佳画质
def pick_best_url(
        aweme_detail: Dict,
        prefer_h264: bool = True
) -> Tuple[Optional[Dict], List[Dict]]:
    """
    返回 (best_item, all_items)。
    规则（可按需要调整）：
      1) 分辨率高者优先（height 2160 > 1440 > 1080 > 720 > 540…）
      2) 同分辨率：FPS 高者优先
      3) 同分辨率 & 同 FPS：码率高者优先
      4) 同上都相同：按编码偏好（默认优先 H.264，其次 ByteVC1/H.265）
    """
    items = list_candidates(aweme_detail)
    if not items:
        return None, []

    def codec_rank(item):
        # 偏好顺序：H.264(0) 最优 → ByteVC1(1) → H.265(2)（你也可以改成相反）
        if prefer_h264:
            return 0 if item["codec"].startswith("H.264") else (1 if "ByteVC1" in item["codec"] else 2)
        else:
            # 若想优先 H.265，把这段改成 H.265 最小排名
            return 0 if item["codec"].startswith("H.265") else (1 if "ByteVC1" in item["codec"] else 2)

    # 排序：分辨率↓, FPS↓, 码率↓, 编码偏好
    items_sorted = sorted(
        items,
        key=lambda x: (
            x["height"],
            x["fps"],
            x["bitrate_kbps"],
            -codec_rank(x)  # 偏好越小越好，所以取负号让它也降序
        ),
        reverse=True
    )
    return items_sorted[0], items_sorted


# 取id
import requests


def get_id(s: str) -> str:
    # 找出分享里的 URL
    import re
    m = re.search(r'(https?://[^\s]+)', s)
    if not m:
        raise ValueError("未找到可用链接")
    url = m.group(1)

    # 关键：用一个 Session，禁用环境代理
    session = requests.Session()
    session.trust_env = False  # 忽略系统/环境变量里的代理
    session.proxies = {"http": None, "https": None}  # 显式不要代理

    resp = session.get(
        url,
        allow_redirects=True,
        timeout=10,
        headers={"User-Agent": "Mozilla/5.0"}
    )
    final = resp.url

    # 提取 aweme_id
    final = final.rstrip("/")
    last = final.split("/")[-1].split("?")[0]
    if last.isdigit():
        return last
    m2 = re.search(r'/video/(\d+)', final)
    if m2:
        return m2.group(1)
    raise ValueError(f"无法解析 aweme_id，原始URL: {final}")


# 处理字典，找出最优结果
def give(data):
    re = {"ok": True,
          "videos": [
              # {
              # "id": "1", "title": "海边日出", "author": "用户A",
              # "duration": "15s", "publish_time": "2025-09-01",
              # "cover_url": "/static/picture/demo_cover.jpg",
              # "page_url": "https://www.douyin.com/video/xxxx",
              # "play_url": "https://www.douyin.com/video/xxxx/play",
              # "download_url": "https://example.com/download/xxxx"
              # }
          ]}
    best, all_items = pick_best_url(data, prefer_h264=True)
    # data = data
    aweme = data.get("aweme_detail", {})

    if not best:
        print("没有找到可用的视频直链。")
    else:
        print("最高画质（自动挑选）:")
        print(f"  分辨率: {best['width']}x{best['height']}, FPS: {best['fps']}, 码率: {best['bitrate_kbps']} kbps")
        print(f"  编码: {best['codec']}, HDR: {best['hdr']}, 源: {best['gear_name']}")
        print(f"  直链: {best['url']}\n")

        # print("所有可用清晰度候选：")
        for i, it in enumerate(all_items, 1):
            if it['hdr']:
                re['videos'].append({'id': str(i),
                                     'title': f"{it['width']}x{it['height']}",
                                     'author': f"{it['fps']}fps",
                                     'duration': f"{it['bitrate_kbps']} kbps",
                                     'publish_time': f"{it['codec']} | {it['gear_name']}",
                                     'play_url': f"{it['url']}",
                                     'download_url': f"{it['url']}",
                                     'url': f"{it['url']}",
                                     'cover_url': '',
                                     'hdr': f"{it['hdr']}",
                                     'codec': f"{it['codec']}"
                                     })

            if not it['hdr']:
                re['videos'].append({'id': str(i),
                                     'title': f"{it['width']}x{it['height']}",
                                     'author': f"{it['fps']}fps",
                                     'duration': f"{it['bitrate_kbps']} kbps",
                                     'publish_time': f"{it['codec']} | {it['gear_name']}",
                                     'play_url': f"{it['url']}",
                                     'download_url': f"{it['url']}",
                                     'url': f"{it['url']}",
                                     'cover_url': '',
                                     'hdr': f"{it['hdr']}",
                                     'codec': f"{it['codec']}"
                                     })
                return re


# def main(s):
#     aweme_id = get_id(s)
#     with sync_playwright() as p:
#         browser = p.chromium.launch(headless=True)
#         context = browser.new_context(
#             user_agent=("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
#                         "AppleWebKit/537.36 (KHTML, like Gecko) "
#                         "Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0"),
#             locale="zh-CN",
#             timezone_id="Asia/Shanghai",
#             viewport={"width": 1366, "height": 768},
#         )
#
#         data = open_page(aweme_id,browser,context)
#         print(data)
#
#     re = give(data)
#
#     return re

def main(s, browser, context):
    aweme_id = get_id(s)

    # 拦截
    context.route("**/*", route_handler)

    data = open_page(aweme_id, browser, context)
    # print(data)

    re = give(data)
    return re


# 开浏览器以及配置模仿

if __name__ == "__main__":

    s = '1.25 复制打开抖音，看看【阿里北杯的作品】就这样偶遇蔡徐坤了…. # 蔡徐坤 # jasmi... https://v.douyin.com/zQuys-GAXrw/ y@g.oD 11/02 foq:/ '
    CUR_DIR = os.path.dirname(__file__)
    PROJ_ROOT = os.path.dirname(CUR_DIR)
    if PROJ_ROOT not in sys.path:
        sys.path.insert(0, PROJ_ROOT)

    try:
        from model.browser import douyin_browser
    except Exception:
        # 如果你的工程外层包名是 GodJin/GodJin，也再试一次
        try:
            from GodJin.model.browser import douyin_browser
        except Exception as e:
            raise ImportError(f"无法导入 douyin_browser，请检查文件名 browser.py 和包结构。原始错误：{e}")

    main(s, douyin_browser.browser, douyin_browser.context)
